import glm
import OpenGL.GL as gl
import numpy as np
import pygl

class PointClouds:
    def __init__(self, positions:np.ndarray, colors:np.ndarray, size=1.0) -> None:
        gl.glEnable(gl.GL_DEPTH_TEST)
        self.point_size = size
        self.num_vertices = len(positions)
        self.program = pygl.ProgramVF("usecase/shaders/point_clouds.vs", 
                                      "usecase/shaders/point_clouds.fs")
        self.vao = pygl.VertexArrayObject()
        self.position_vbo = pygl.VertexBufferObject(positions)
        self.color_vbo = pygl.VertexBufferObject(colors)
        self.vao.setVertexBuffer(self.position_vbo, 0, 0, 
                                 3 * gl.sizeof(gl.GLfloat))
        self.vao.setVertexBuffer(self.color_vbo, 1, 0, 
                                 3 * gl.sizeof(gl.GLfloat))
        attribute_position = pygl.VertexAttribute("position", 0, 3, 
                                                  gl.GL_FLOAT, False, 0)
        attribute_color = pygl.VertexAttribute("color",1, 3, gl.GL_FLOAT, 
                                False, 3 * gl.sizeof(gl.GLfloat))
        self.vao.setVertexAttribute(0, attribute_position)
        self.vao.setVertexAttribute(1, attribute_color)
        self.rotation = glm.vec3(0.0, 0.0, 0.0)
        self.position = glm.vec3(0.0, 0.0, 0.0)
        self.scale = glm.vec3(1.0, 1.0, 1.0)
        self.updateModelMatrix()

    def render(self, view_matrix: glm.mat4, projection_matrix: glm.mat4) -> None:
        self.program.use()
        self.program.setUniformMatrix4fv(0, self.model_matrix)
        self.program.setUniformMatrix4fv(1, view_matrix)
        self.program.setUniformMatrix4fv(2, projection_matrix)
        self.vao.bind()
        gl.glPointSize(self.point_size)
        gl.glDrawArrays(gl.GL_POINTS, 0, self.num_vertices)

    def delete(self)->None:
        self.vao.delete()
        self.position_vbo.delete()
        self.color_vbo.delete()
        self.program.delete()

    def updateModelMatrix(self)->None:
        scale_matrix = glm.scale(glm.mat4(1.0), self.scale)
        rotation_matrix = glm.rotate(glm.mat4(1.0), glm.radians(self.rotation.x), 
                                     glm.vec3(1.0, 0.0, 0.0))
        rotation_matrix = glm.rotate(rotation_matrix, 
                        glm.radians(self.rotation.y), glm.vec3(0.0, 1.0, 0.0))
        rotation_matrix = glm.rotate(rotation_matrix, 
                        glm.radians(self.rotation.z), glm.vec3(0.0, 0.0, 1.0))
        translation_matrix = glm.translate(glm.mat4(1.0), self.position)
        self.model_matrix = translation_matrix * rotation_matrix * scale_matrix